<?php

/*
 * Copyright (C) 2009 - 2011 Pham Cong Dinh
 *
 * This file is part of Spica.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

// namespace spica\core\test;

/**
 * Prepares a mock environment for unit testing.
 *
 * namespace spica\core\test\MockEnvironment
 *
 * @category   spica
 * @package    core
 * @subpackage test
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 16, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Test.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMockEnvironment
{
    /**
     * Relative path to bootstrap file.
     *
     * @var string
     */
    protected $_bootstrapPath;

    /**
     * Application name.
     *
     * @var string
     */
    protected $_app;

    /**
     * Full qualified request URL.
     *
     * @var string
     */
    protected $_url;

    /**
     * User agent string.
     *
     * @var sring
     */
    protected $_userAgent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8';

    /**
     * Remote address of the request.
     *
     * @var string
     */
    protected $_remoteAddress = '127.0.0.1';

    /**
     * The HTTP method that used to send the request.
     *
     * @var string
     */
    protected $_requestMethod = 'GET';

    /**
     * Document root physical path to web root directory.
     *
     * @var string
     */
    protected $_documentRoot;

    /**
     * The subdirectory in which to look for index.php.
     * This path is relative to document root directory.
     *
     * @var string
     */
    protected $_subdir;

    /**
     * POST data.
     *
     * @var array
     */
    protected $_post = array();

    /**
     * Is connection secure.
     *
     * @var bool
     */
    protected $_isSecure = false;

    /**
     * Constructs an object of <code>SpicaMockEnvironment</code>
     *
     * @param string $bootstrapPath
     * @param string $app Application name
     */
    public function __construct($bootstrapPath, $app = 'default')
    {
        $this->_bootstrapPath = str_replace('\\', '/', $bootstrapPath);
        $this->_app = $app;
    }

    /**
     * Sets the subdirectory in which to look for index.php file (bootstrap file).
     *
     * This path is relative to document root directory. It is a very important
     * settings when testing virtual host and alias path.
     *
     * @param string $path
     */
    public function setSubdirectory($path)
    {
        $this->_subdir = $path;
    }

    /**
     * Sets full qualified URL path.
     *
     * @param string $url E.x: http://a.domain.com/subdir/home/index/index
     */
    public function setUrl($url)
    {
        $this->_url = rtrim($url, '/').'/';
    }

    /**
     * Sets user agent of the request.
     *
     * @param string $userAgent
     */
    public function setUserAgent($userAgent)
    {
        $this->_userAgent = $userAgent;
    }

    /**
     * Sets remote address.
     *
     * @param string $address
     */
    public function setRemoteAddress($address)
    {
        $this->_remoteAddress = $address;
    }

    /**
     * Sets POST data.
     *
     * @param array $post
     */
    public function getPost(array $post)
    {
        $this->_post = $post;
    }

    /**
     * Sets secure mode to the connection.
     *
     * @param bool $secure
     */
    public function setSecure($secure)
    {
        $this->_isSecure = $secure;
    }

    /**
     * Sets up Spica environment.
     */
    public function run()
    {
        $this->resetSpicaState();
        $this->_setupEnvironment();
        // Load configuration
        include dirname($this->_bootstrapPath).'/apps/'.$this->_app.'/config/global.php';
        // Registers current bootstrapping path
        $GLOBALS['__state__']['bootstrap_path'] = dirname($this->_bootstrapPath);
        $GLOBALS['__state__']['app_path']       = dirname($this->_bootstrapPath).'/apps/'.$this->_app;
        SpicaContext::selectPackage();
    }

    /**
     * Sets up server variable environment.
     */
    protected function _setupEnvironment()
    {
        if (null === $this->_subdir)
        {
            throw new Exception('Relative subdirectory in the request must be set
            or we dont know where real request path starts. ');
        }

        set_include_path(get_include_path().PATH_SEPARATOR.dirname($this->_bootstrapPath).PATH_SEPARATOR.dirname($this->_bootstrapPath).'/library');

        $parts = parse_url($this->_url);
        $query = '';

        if (true === isset($parts['query']))
        {
            $query   = '?'.$parts['query'];
            $queries = explode('&', $parts);
            // Simulates GET
            foreach ($queries as $query)
            {
                $kv  = explode('=', $query); // key-value
                if (isset($kv[0]) && isset($kv[1]))
                {
                    $_GET[$kv[0]] = $kv[1];
                }
            }
        }

        // Something like this "/repos/spica/trunk/index.php"
        $_SERVER["SCRIPT_NAME"]     = $this->_subdir.'/index.php';
        $_SERVER["SERVER_PORT"]     = !empty($parts['port'])?$parts['port']:'80';
        $_SERVER["HTTP_USER_AGENT"] = $this->_userAgent;
        $_SERVER["REMOTE_ADDR"]     = $this->_remoteAddress;
        // Like this "D:/webroot/repos/spica/trunk/index.php"
        $_SERVER["SCRIPT_FILENAME"] = $this->_bootstrapPath;
        $_SERVER["REQUEST_METHOD"]  = $this->_requestMethod;
        $_SERVER["REQUEST_URI"]     = $parts['path'].$query;
        $_SERVER["PHP_SELF"]        = $parts['path'];
        $_SERVER["HTTP_HOST"]       = $parts['host'];
        $_SERVER["SERVER_NAME"]     = $parts['host'];
        $_SERVER["HTTPS"]           = (true === $this->_isSecure)?'on':'off';
        putenv('HTTP_HOST='.$parts['host']);
    }

    /**
     * Resets Spica application state.
     */
    protected function resetSpicaState()
    {
        restore_error_handler();
        restore_exception_handler();
        // Global variables are reset.
        $_POST = $this->_post;
        $_GET  = array();
        // Reset Spica application state
        unset($GLOBALS['__state__']);
        unset($GLOBALS['__profiles__']);
        unset($GLOBALS['__mail__']);
        unset($GLOBALS['__response__']);
        unset($GLOBALS['__error__']);
        unset($GLOBALS['__event__']);
        unset($GLOBALS['__undeclared_events__']);
        unset($GLOBALS['__t__']);
        unset($GLOBALS['__ds__']);
    }
}

/**
 * This class provies set of methods to allow simulate request that sends to
 * Spica Core System to test the internal behaviors of the system.
 *
 * namespace spica\core\test\MockRequestSimulator
 *
 * @category   spica
 * @package    core
 * @subpackage test
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 12, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Test.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMockRequestSimulator extends SpicaMockEnvironment
{
    /**
     * Starts loading bootstrap file to make a non-HTTP request to Spica system.
     *
     * @return string
     */
    public function run()
    {
        $this->resetSpicaState();
        $this->_setupEnvironment();
        ob_start();
        $this->bootstrap(); // do not include bootstrap path here because static directory name resolution
        return ob_get_clean();
    }

    /**
     * Executes bootstrap procedures.
     */
    public function bootstrap()
    {
        $dir = dirname($this->_bootstrapPath);
        set_include_path($dir);
        include_once $dir.'/library/spica/core/Base.php';

        try
        {
            SpicaContext::start($dir);
            SpicaResponse::send();
            SpicaContext::stop();
        }
        catch (Exception $e)
        {
            SpicaContext::catchException($e);
        }
    }
}

/**
 * This class provies set of methods to allow change configuration and
 * simulate internal calls to Spica Core System to test the internal
 * behaviors of the system.
 *
 * namespace spica\core\test\MockConfigurationSimulator
 *
 * @category   spica
 * @package    core
 * @subpackage test
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 25, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Test.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaMockConfigurationSimulator extends SpicaMockEnvironment
{
    /**
     * Active profile.
     *
     * @var string
     */
    protected $_activeProfile;

    /**
     * The combination of module, controller, action names to handle a request that
     * can not be served by any conventional module, controller, name.
     *
     * @var array
     */
    protected $_nonrouteConfig;

    /**
     * Sets active profile as if it is configured in global.php file.
     *
     * @param string $name
     */
    public function setActiveProfile($name)
    {
        $this->_activeProfile = $name;
    }

    /**
     * Sets a combination of module, controller, action names that is configured to
     * handle a request that can not be served by any combination of conventional
     * module, controller and action names.
     *
     * @param array $config
     */
    public function setNonRoute($config)
    {
        $this->_nonrouteConfig = $config;
    }

    /**
     * Starts overriding some Spica configuration variables.
     *
     * @return string
     */
    public function run()
    {
        $this->resetSpicaState();
        $this->_setupEnvironment();
        ob_start();
        $basePath = dirname($this->_bootstrapPath);
        include_once $basePath.'/library/spica/core/Base.php';
        include $basePath.'/apps/'.$this->_app.'/config/global.php';

        $GLOBALS['__state__']['bootstrap_path'] = $basePath;
        $GLOBALS['__state__']['app_path']       = $basePath.'/apps/'.$this->_app;
        SpicaContext::selectPackage();

        if (null !== $this->_activeProfile)
        {
            $GLOBALS['__state__']['profile'] = $this->_activeProfile;
        }

        if (null !== $this->_nonrouteConfig)
        {
            $GLOBALS['__profiles__'][$GLOBALS['__state__']['profile']]['backup_route'] = $this->_nonrouteConfig;
        }

        return ob_get_clean();
    }
}

?>